/**
 * 
 */
package com.gragra.sampling.binary;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import java.util.Arrays;
import com.gragra.sampling.PopulationProbabilityAssigner;
import com.gragra.sampling.ThreadedUniformAccess;
import com.gragra.sampling.sequentialStructure.WordAndTagHolder;
/**
 * @author Christoph Teichmann
 * 15.09.2013
 */
public class BinaryExtendedGibbs extends BinaryStructure
{
	/**
	 * 
	 */
	private final double[] probs;
	/**
	 * @param wth
	 * @param threads
	 * @param cs
	 */
	public BinaryExtendedGibbs(WordAndTagHolder wth, int threads,
			ConvertSample cs, boolean samplesHead)
	{
		super(wth, threads, cs,samplesHead);
		this.probs = new double[this.getLength()];
	}
	/* (non-Javadoc)
	 * @see com.gragra.sampling.binary.BinarySplitGibbs#coreSampling(double, int, com.gragra.sampling.ThreadedUniformAccess, com.gragra.sampling.binary.BinaryAssigner)
	 */
	@Override
	protected void coreSampling(double annealing, int thread,
			ThreadedUniformAccess tua, PopulationProbabilityAssigner ba)
	{extendedSampling(annealing, thread, tua, ba);}
	/**
	 * @param annealing
	 * @param thread
	 * @param tua
	 * @param ba
	 * @throws IllegalStateException
	 */
	private void extendedSampling(double annealing, int thread,
			ThreadedUniformAccess tua, PopulationProbabilityAssigner ba)
			throws IllegalStateException
	{
		todo.clear();
		todo.add(this.getRootPosition());
		for(int i=0;i<this.todo.size();++i)
		{
			int node = todo.getInt(i);
			this.windLeft(thread, node);
			Arrays.fill(this.probs, 0.0);
			int pos = 0;
			double max = this.probs[pos++] = ba.makeLogProb(this, thread)*annealing;
			while(this.getDominated(node, false, thread) >= this.getLength())
			{
				this.flipLeft(node, thread);
				max = Math.max(this.probs[pos++] = ba.makeLogProb(this, thread)*annealing,max);
			}
			this.windLeft(thread, node);
			double sum = 0.0;
			for(int p=0;p<pos;++p)
			{sum += this.probs[p] = Math.exp(probs[p]-max);}
			int choice = -1;
			double border = tua.nextDouble(thread);
			for(int p=0;p<pos;++p)
			{
				border -= this.probs[p]/sum;
				if(border <= 0.0)
				{
					choice = p;
					break;
				}
			}
			if(choice < 0)
			{throw new IllegalStateException("probabilites did not sum to one");}
			for(int p=0;p<choice;++p)
			{this.flipLeft(node, thread);}
			extendTodo(thread, node, true);
			extendTodo(thread, node, false);
		}
	}
	/**
	 * @param thread
	 * @param node
	 * @param left
	 */
	private void extendTodo(int thread, int node, boolean left)
	{
		int n = this.getDominated(node, left, thread);
		if(n>=this.getLength())
		{todo.add(n);}
	}
	/**
	 * 
	 */
	private final IntArrayList todo = new IntArrayList();
	/**
	 * @param thread
	 * @param node
	 */
	private void windLeft(int thread, int node)
	{
		while(this.getDominated(node, true, thread) >= this.getLength())
		{this.flipRight(node, thread);}
	}
}